from typing import Optional
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from starlette import status

app = FastAPI()
# 模拟数据库
fake_users_db = {
    "lxgzhw001": {
        "username": "lxgzhw001",
        "full_name": "Dapeng Zhang",
        "email": "lxghzw001@example.com",
        "hashed_password": "cuicui_lxgzhw001",
        "disabled": False,
    },
    "lxgzhw002": {
        "username": "lxgzhw002",
        "full_name": "Dapeng Zhang",
        "email": "lxghzw002@example.com",
        "hashed_password": "cuicui_lxgzhw002",
        "disabled": False,
    },
}


# 密码加密
def fake_hash_password(password: str):
    # cuicui_密码前缀
    # 简单加密
    return "cuicui_" + password


oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


# 创建用户模型
class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None


# 用户入库模型
class UserInDB(User):
    hashed_password: str


# 获取用户
def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


# 生成一个token
def fake_decode_token(token):
    # 返回用户
    # 这里将token当成了用户名
    # 这里的fake_users_db是全局变量，而不是传过来的变量
    user = get_user(fake_users_db, token)
    return user


# 获取当前用户
async def get_current_user(token: str = Depends(oauth2_scheme)):
    # 这里的token是用户名
    # user是一个UserInDb对象
    user = fake_decode_token(token)
    # 如果每页获取到，就报错
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication credentials",
            headers={"WWW-Authenticate": "Bearer"},
        )
    # 获取到了，就返回用户对象
    return user


# 获取当前活跃用户
async def get_current_active_user(current_user: User = Depends(get_current_user)):
    # 如果当前用户不可用，就抛出异常
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    # 可用，就返回当前用户
    return current_user


# 生成token
# 参数是用auth表单
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    # 根据用户名获取用户字典
    user_dict = fake_users_db.get(form_data.username)
    # 如果没有获取到，就报错
    if not user_dict:
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    # 转换为UserInDB对象
    user = UserInDB(**user_dict)
    # 得到hash密码
    hashed_password = fake_hash_password(form_data.password)
    # 如果不一致，就报错
    if not hashed_password == user.hashed_password:
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    # 都不报错，则返回用户名（token）
    return {"access_token": user.username, "token_type": "bearer"}


# 获取当前用户
# 这里会根据依赖自动检查权限，检查通过以后才会触发接口
# 注意：需要在doc中点击auth输入账号密码登录以后，此接口才会解锁
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user
